<template>
    <div class="textTovoice">
        <div class="navtitle">
            <span>文本转语音服务</span>
        </div>
        <div class="tit">
            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;文本转语音可让应用程序、工具或设备将文本转换为类似于人类的合成语音。 文本转语音功能也称为语音合成。
            使用类似于人类的现成预生成神经网络声音，或根据你的产品或品牌创建独特的神经网络定制声音。建议在PC端使用。
        </div>
        <div class="woded">
            我的额度(单位:字数)
        </div>
        <div class="edcontent">

            <div><span>额度:{{ quota }}</span>&nbsp;&nbsp;
                <span>剩余:{{ Number(quota) - Number(usage) }}</span>
            </div>
        </div>
        <div class="shiting">试听</div>
        <div>
            <a-select v-model:value="value" placeholder="请选择" @change="changeLan" style="margin-right: 15px; width: 200px;">
                <a-select-option v-for="item in optionsLan" :key="item.id" :value="item.id">{{ item.name }}
                </a-select-option>
            </a-select>
            <a-select v-model:value="sex" placeholder="请选择" style="margin-right: 15px; width: 200px;" @change="changeSex">
                <a-select-option v-for="(item, index) in optionsSex" :key="index" :value="item">{{ item }}
                </a-select-option>
            </a-select>
            <a-button type="primary" @click="clear()" style="margin-left:10px;">清除筛选</a-button>
        </div>
        <div class="content">
            <div class="content-left">
                <div class="item " :class="active == index ? 'active' : ''" v-for="(item, index) in voiceData" :key="index"
                    @click="handle(item, index)">
                    <div class="box1">

                        <img v-if="item.sex == '男'" src="/static/svg/users-2.svg" />
                        <img v-if="item.sex == '女'" src="/static/svg/users-14.svg" />
                        <div>
                            <span>{{ item.name }}</span>
                            <span>{{ item.sex }}</span>
                        </div>

                    </div>
                    <div class="box-center">
                        <p class="tip">{{ item.languageName }}</p><br />
                        <p class="tip">{{ item.count }} 种风格</p>
                    </div>
                    <div class="box-bottom">
                        {{ item.description }}
                    </div>

                </div>

            </div>
            <div class="content-right">
                <p>说话风格</p>
                <a-radio-group v-model:value="style" size="mini">
                    <a-radio border v-for="(item, index) in styleData" :key="index" :value="item.id"
                        @change="handleval(item)">{{ item.name }}</a-radio>

                </a-radio-group>
                <p>角色</p>
                <a-radio-group v-model:value="role" size="mini">
                    <a-radio border v-for="(item, index) in rolesData" :key="index" :value="item.id"
                        @change="handleval1(item)">{{ item.name }}</a-radio>
                </a-radio-group>
            </div>
        </div>
        <div class="title1">
            <span v-if="items && items.languageName"> 您的选择是：{{ items.languageName }}，{{ items.name }}，</span><span
                v-if="styleName != ''">说话风格({{ styleName }})</span><span v-if="roleName != ''">，角色风格({{ roleName }})</span>
        </div>
        <div class="box-content2">
            <a-textarea type="textarea" placeholder="请输入内容" v-model:value="textarea" rows="5" maxlength="200" show-word-limit>
            </a-textarea>
            <a-button class="st" type="primary" @click="synThesize()">试听</a-button>
        </div>

        <audio v-if="audioData" controls="controls" autobuffer="autobuffer" autoplay="autoplay">
            <source :src="'data:audio/wav;base64, ' + audioData" />
        </audio>
        <p class="tt">注意:试听仅限200字。</p>
        <div class="voice">
            <div class="top"><span class="top-left">生成语音文件</span> <span class="top-right">我的用量/额度 {{ usage }} /
                    {{ quota }}</span></div>
            <a-textarea class="text-box" type="textarea" :maxlength="Number(quota) - Number(usage)" rows="5" placeholder="请输入内容"
                show-word-limit v-model:value="textarea1">
            </a-textarea>
            <div class="bottom">
                <p class="tt">输入字符数:{{ textarea1.length }} / {{ Number(quota)
                    - Number(usage) }}<br />注意:每个账号免费字数为20000字，如需继续使用，需要付费购买。</p><a-button class="st" type="primary"
                    @click="synAutio()">生成并下载音频文件</a-button>
            </div>
        </div>
    </div>
</template>
<script>

import {
    gettexttovoicelan, getalltexttovoice, getalltexttovoicestylebyvoiceid, getalltexttovoicerolebyvoiceid,

    synthesize,
    texttovoicetest,
    getusage

} from "@/api/index"
export default {
    data() {
        return {
            radio4: '1',
            textarea: '',
            textarea1: '',
            optionsLan: [],
            optionsSex: ['男', '女'],
            voiceData: [],
            sex: '',
            value: '',
            active: 100000,
            styleData: [],
            rolesData: [],
            style: '',
            styleName: '',
            role: '',
            roleName: '',
            items: {},
            voiceItems: {},
            styleItems: {},
            roleItems: {},
            audioData: '',
            audioData1: '',
            quota: '',
            usage:''
        }
    },
    methods: {
        clear(){
            this.getTexttovoicelan();
            this.sex=''
            this.value=''
        },
        getUsage(){
          
            getusage( ).then(res=>{
               
                this.quota=res.data.quota
                this.usage=res.data.usage
            })
        },
        synThesize() {
            this.audioData =''
            if (this.textarea.length == 0) {
                this.$message({
                    message: '内容不能为空',
                    type: 'info'
                })
                return
            }
           
            if (this.items.languaeValue == undefined) {
                this.$message({
                    message: '请选择语言',
                    type: 'info'
                })
                return
            }
            let data = {
                voiceLanguageValue: this.items.languaeValue,
                voiceValue: this.items.value,
                voiceStyleValue: this.styleItems.value,
                voiceRoleValue: this.roleItems.value,
                voiceContent: this.textarea
            }
           
            texttovoicetest(data).then(res => {
                // console.log(res)
                this.audioData = res.data
            })
        },
        synAutio() {
            let data = {
                voiceLanguageValue: this.items.languaeValue,
                voiceValue: this.items.value,
                voiceStyleValue: this.styleItems.value,
                voiceRoleValue: this.roleItems.value,
                voiceContent: this.textarea1
            }

            synthesize(data).then(res => {
                this.audioData1 = res.data
               
                const reader = new FileReader();
reader.readAsArrayBuffer(res.data);
reader.onload = (e) => {
   const bufer = e.srcElement.result;
   this.audioData1=this.addWavHeader(bufer, 16000, 16, 1)
//    const blob = this.addWavHeader(bufer, 16000, 16, 1);
   console.log(this.addWavHeader(bufer, 16000, 16, 1))
//    this.srcUrl = window.URL.createObjectURL(blob);
var blob = new Blob([res.data], {type: 'audio/wav' }); //type这里表示xlsx类型
var link = document.createElement('a');
var href = window.URL.createObjectURL(blob); //创建下载的链接
link.href = href;
link.download = this.items.value+'-'+new Date()+'.wav'; //下载后文件名
document.body.appendChild(link);
link.click(); //点击下载
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
}
                // this.downloadFile(res.data, 'dddd')
            })
        },
        handleval(val) {
            console.log(val);
            this.styleItems = val;
            this.styleName = val.name

        },
        handleval1(val) { 
            this.roleName = val.name; 
            this.roleItems = val;
        },
        changeLan(value) {

            getalltexttovoice(value).then(res => {
                console.log(this.sex)
                this.voiceData = res.data;
                this.voiceData = this.voiceData.filter(e => {

                    if (this.sex != '') {

                        if (e.languageid == value && this.sex == e.sex) {

                            return e
                        }

                    } else {

                        if (e.languageid == value) {
                            return e
                        }
                    }

                })

            })



        },
        changeSex(value) {
            if (value == '全部') {
                this.getTexttovoicelan()
          
            } else {
                getalltexttovoice(value).then(res => {
                    // console.log(res)
                    this.voiceData = res.data;
                    this.voiceData = this.voiceData.filter(e => {
                        if (this.value == '') {
                            if (e.sex == value) {
                                return e
                            }
                        } else {
                            if (e.sex == value && this.value == e.languageid) {
                                return e
                            }
                        }

                    })
                })
            }

        },
        handle(item, index) {
            this.active = index
            console.log(item)
            this.items = item
            getalltexttovoicestylebyvoiceid(item.id).then(res => {
                this.styleData = res.data
            })
            getalltexttovoicerolebyvoiceid(item.id).then(res => {
                this.rolesData = res.data
            })
        },
        getTexttovoicelan() {
            gettexttovoicelan().then(res => {

                this.optionsLan = res.data
                // this.value=res.data[0].id
            })
            getalltexttovoice().then(res => {
                // console.log(res)
                this.voiceData = res.data;

            })
        },
        // 点击下载调用此method
        // downloadFile(audioFile) {
        //     const a = document.createElement('a');
        //     a.download = `ddd.wav`;
        //     a.href = window.URL.createObjectURL(audioFile);
        //     a.click();
        // },
        download(url, name) {
            const aLink = document.createElement('a')
            aLink.download = name
            aLink.href = url
            // aLink.dispatchEvent(new MouseEvent('click', {}))
            document.body.appendChild(aLink)
            aLink.click()
            document.body.removeChild(aLink)
            URL.revokeObjectURL(url) //释放url对象
        }
        ,
        downloadFile(res, fileName) { // res为后端传来的文件流，// fileName为文件名称，自己根据实际情况赋值
            if (!res) {
                return
            }
            if (window.navigator.msSaveBlob) { // IE以及IE内核的浏览器
                try {
                    window.navigator.msSaveBlob(res, fileName) // res为接口返回数据，这里请求的时候已经处理了，如果没处理需要在此之前自行处理var data = new Blob([res.data]) 注意这里需要是数组形式的,fileName就是下载之后的文件名
                    // window.navigator.msSaveOrOpenBlob(res, fileName);  //此方法类似上面的方法，区别可自行百度
                } catch (e) {
                    console.log(e)
                }
            } else {
                let url = window.URL.createObjectURL(new Blob([res], {
                    type: 'mav' // 前后端一定要同意utf-8编码，否则会是乱码
                }));
                console.log(url)
                let link = document.createElement('a')
                link.style.display = 'none'
                link.href = url
                link.setAttribute('download', fileName) // 文件名
                document.body.appendChild(link)
                link.click()
                document.body.removeChild(link) // 下载完成移除元素
                window.URL.revokeObjectURL(url) // 释放掉blob对象
            }
        },
        addWavHeader(samples, sampleRateTmp, sampleBits, channelCount) {
      const dataLength = samples.byteLength;
      /* 新的buffer类，预留44bytes的heaer空间 */
      const buffer = new ArrayBuffer(44 + dataLength);
      /* 转为 Dataview, 利用API来填充字节 */
      const view = new DataView(buffer);
      let offset = 0;
      /* ChunkID, 4 bytes,  资源交换文件标识符 */
      this.writeString(view, offset, 'RIFF'); offset += 4;
      /* ChunkSize, 4 bytes, 下个地址开始到文件尾总字节数,即文件大小-8 */
      view.setUint32(offset, /* 32 */ 36 + dataLength, true); offset += 4;
      /* Format, 4 bytes, WAV文件标志 */
      this.writeString(view, offset, 'WAVE'); offset += 4;
      /* Subchunk1 ID, 4 bytes, 波形格式标志 */
      this.writeString(view, offset, 'fmt '); offset += 4;
      /* Subchunk1 Size, 4 bytes, 过滤字节,一般为 0x10 = 16 */
      view.setUint32(offset, 16, true); offset += 4;
      /* Audio Format, 2 bytes, 格式类别 (PCM形式采样数据) */
      view.setUint16(offset, 1, true); offset += 2;
      /* Num Channels, 2 bytes,  通道数 */
      view.setUint16(offset, channelCount, true); offset += 2;
      /* SampleRate, 4 bytes, 采样率,每秒样本数,表示每个通道的播放速度 */
      view.setUint32(offset, sampleRateTmp, true); offset += 4;
      /* ByteRate, 4 bytes, 波形数据传输率 (每秒平均字节数) 通道数×每秒数据位数×每样本数据位/8 */
      view.setUint32(offset, sampleRateTmp * channelCount * (sampleBits / 8), true); offset += 4;
      /* BlockAlign, 2 bytes, 快数据调整数 采样一次占用字节数 通道数×每样本的数据位数/8 */
      view.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2;
      /* BitsPerSample, 2 bytes, 每样本数据位数 */
      view.setUint16(offset, sampleBits, true); offset += 2;
      /* Subchunk2 ID, 4 bytes, 数据标识符 */
      this.writeString(view, offset, 'data'); offset += 4;
      /* Subchunk2 Size, 4 bytes, 采样数据总数,即数据总大小-44 */
      view.setUint32(offset, dataLength, true); offset += 4;
      if (sampleBits === 16) {
        this.floatTo16BitPCM(view, samples);
      } else if (sampleBits === 8) {
        this.floatTo8BitPCM(view, samples);
      } else {
        this.floatTo32BitPCM(view, samples);
      }
      return new Blob([view], { type: 'audio/wav' });
    },
    writeString(view, offset, string) {
      for (let i = 0; i < string.length; i += 1) {
        view.setUint8(offset + i, string.charCodeAt(i));
      }
    },
floatTo32BitPCM(output, input) {
      const oinput = new Int32Array(input);
      let newoffset = 44;
      for (let i = 0; i < oinput.length; i += 1, newoffset += 4) {
        output.setInt32(newoffset, oinput[i], true);
      }
    },
floatTo16BitPCM(output, input) {
      const oinput = new Int16Array(input);
      let newoffset = 44;
      for (let i = 0; i < oinput.length; i += 1, newoffset += 2) {
        output.setInt16(newoffset, oinput[i], true);
      }
    },
floatTo8BitPCM(output, input) {
      const oinput = new Int8Array(input);
      let newoffset = 44;
      for (let i = 0; i < oinput.length; i += 1, newoffset += 1) {
        output.setInt8(newoffset, oinput[i], true);
      }
    },

 



    },
    mounted() {
        this.getTexttovoicelan()
        this.getUsage()
        document.title = '文本转语音'
    }
}
</script>
<style lang="less" scoped>
.textTovoice {
    padding: 30px;
    width: 1260px;
    max-width: 100%;
    margin: 0 auto;
    .box-center {
        margin-top: 25px;

        .tip {
            display: inline-block;
            background: #fff;
            font-size: 14px;
            color: rgba(56, 23, 129, 1);
            border-radius: 13px;
            border: solid 1px #707070;
            padding: 5px 14px;
            margin-bottom: 10px;

        }

    }

    .navtitle {
        padding-top: 10px;
        padding-bottom: 15px;
        display: flex;
        align-items: center;

        span {
            font-size: 26px;
            font-weight: bold;
            color: rgba(56, 23, 129, 1)
        }

        img {
            width: 14px;
            height: 24px;
            margin: 0 5px;
        }
    }

    .tit {
        height: 132px;
        box-sizing: border-box;
        padding: 20px 10px;
        background: #E9ECEF;
        font-size: 16px;
        border-radius: 6px;
     
        color: rgba(56, 23, 129, 1);
        line-height: 28px;
    }

    .woded {
        padding-top: 10px;
        padding-bottom: 15px;
        font-size: 20px;
        ;
        font-weight: bold;
        color: rgba(56, 23, 129, 1);
    }

    .edcontent {
        width: 334px;
        height: 55px;
        display: inline-block;
        background: #E9ECEF;
        display: flex;
        align-items: center;
        padding: 5px 5px 5px 10px;
        display: flex;
        justify-content: space-between;
        box-sizing: border-box;
        border-radius: 6px;
        /deep/.el-button{
            border-radius: 6px;
        }

        span {
            font-size: 16px;
            font-weight: bold;
            color: rgba(56, 23, 129, 1);

            &.cz {}

        }
    }

    .shiting {
        font-size: 18px;
        ;
        color: rgba(56, 23, 129, 1);
        padding: 7px;
    }

    .content {
        display: flex;
        justify-content: space-between;
        margin-top: 10px;
        align-items: center;

        .content-left {
            border: solid 1px rgba(0, 0, 0, 0.47);
            padding-left: 8px;
            width: 838px;

            justify-content: flex-start;
            flex-wrap: wrap;
            max-height: 748px;
            align-items: flex-start;
            height: 748px;
            overflow-y: scroll;

            &::-webkit-scrollbar {

                width: 8px;

            }

            &::-webkit-scrollbar-track {

                background-color: #fff;

                -webkit-border-radius: 2em;

                -moz-border-radius: 2em;

                border-radius: 2em;

            }

            &::-webkit-scrollbar-thumb {

                background-color: #707070;

                -webkit-border-radius: 2em;

                -moz-border-radius: 2em;

                border-radius: 2em;

            }

            .item {
                width: 260px;
                height: 231px;
                margin: 6px 6px;
                padding: 10px;
                box-sizing: border-box;
                box-shadow: 0px 0px 6px 0 rgb(0 0 0 / 20%);
                cursor: pointer;
                display: inline-block;
                float: left;

                &:hover {
                    box-shadow: 0px 0px 6px 0 rgb(0 0 0 / 40%);
                }

                &.active {
                    background: #AFEB98;
                }

                .box1 {
                    display: flex;
                    justify-content: space-between;

                    img {
                        width: 53px;
                        height: 53px;
                    }

                    div {
                        flex: 1;
                        display: flex;
                        align-items: center;
                        flex-wrap: wrap;
                        margin-left: 30px;

                        span {
                            width: 100%;
                            display: flex;
                            // justify-content: center;
                        }
                    }
                }

                .box-bottom {
                    font-size: 14px;
                    color: rgba(56, 23, 129, 1);

                }
            }
        }

        .content-right {
            width: 345px;
            border: solid 1px rgba(0, 0, 0, 0.47);
            margin-left: 5px;
            max-height: 748px;
            height: 748px;
            padding: 0px 10px;
            overflow-y: scroll;

            p {
                color: rgba(56, 23, 129, 1);
                font-size: 14px;
                padding-top: 20px;
                padding-bottom: 15px;
            }

            /deep/.el-radio {
                margin-right: 0px;
                width: 158px;
                background: #CED4DA;
                margin-left: 0px;
                margin-right: 2px;
                margin-top: 5px;

                .el-radio__inner {
                    border-radius: 4px;

                }

                .el-radio__input.is-checked .el-radio__inner {
                    border-color: #4019E5;
                    background: #4019E5;

                    &::after {
                        background-color: #4019E5;
                    }
                }

                .el-radio__label {
                    color: rgba(56, 23, 129, 1);
                    font-size: 14px;
                }

                &.el-radio--mini.is-bordered {
                    padding: 12px 15px 0 10px;
                    border-radius: 3px;
                    height: 38px;
                }

                &.is-bordered {
                    border: solid 1px none;

                    &.is-checked {
                        background: #AFEB98 !important;
                        border: solid 1px #CED4DA;
                    }

                }

            }
        }
    }

    .title1 {
        font-size: 16px;
        color: rgba(56, 23, 129, 1);
        padding: 5px 0px;
    }

    .box-content2 {
        display: flex;
        justify-content: space-between;

        /deep/.el-textarea__inner {
            border: 1px solid #4A14FF;
        }

        .st {
            margin-left: 10px;
        }
    }

    .tt {
        font-size: 12px;
        color: #000;
        margin-top: 5px;
    }

    .voice {
        margin-top: 60px;

        .top {
            display: flex;
            justify-content: space-between;
            align-items: center;

            .top-left {
                font-size: 20px;
                color: #4019E5;
                font-weight: bold;
            }

            .top-right {
                font-size: 18px;
                color: #000;
            }
        }

        .text-box {

            margin-top: 10px;

            /deep/ .el-textarea__inner {
                height: 300px;
                border: 1px solid #4A14FF;
            }
        }

        .bottom {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;

            .tt {
                font-size: 12px;
                color: #000;
                margin-top: 5px;
                margin-bottom: 40px;
            }

            .st {
                margin-top: 10px;
            }
        }

    }
}
</style>